package com.karaoke.android.player

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.net.ConnectivityManager
import android.net.NetworkInfo
import android.os.Environment
import android.os.IBinder
import android.view.Surface
import com.apkfuns.logutils.LogUtils
import com.github.pwittchen.reactivenetwork.library.Connectivity
import com.github.pwittchen.reactivenetwork.library.ReactiveNetwork
import com.karaoke.android.data.domain.Song
import com.karaoke.android.data.service.MediaListService
import com.karaoke.android.player.videocache.CacheListener
import com.karaoke.android.player.videocache.HttpProxyCacheServer
import com.orhanobut.hawk.Hawk
import rx.Observable
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import tv.danmaku.ijk.media.player.IMediaPlayer
import tv.danmaku.ijk.media.player.IjkMediaMeta
import tv.danmaku.ijk.media.player.IjkMediaPlayer
import tv.danmaku.ijk.media.player.misc.ITrackInfo
import tv.danmaku.ijk.media.player.misc.IjkTrackInfo
import java.io.File
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.properties.Delegates


/**
 * Created by allen on 2017/5/2.
 */

object VideoManager : IMediaPlayer.OnPreparedListener, IMediaPlayer.OnCompletionListener,
        IMediaPlayer.OnBufferingUpdateListener, IMediaPlayer.OnSeekCompleteListener, IMediaPlayer.OnErrorListener,
        IMediaPlayer.OnVideoSizeChangedListener, IMediaPlayer.OnInfoListener, CacheListener, ServiceConnection
{
    override fun onServiceDisconnected(name: ComponentName?)
    {
    }

    override fun onServiceConnected(name: ComponentName?, service: IBinder?)
    {
        mService = (service as MediaPlayerService.LocalBinder).service
    }

    var videoPlayer: IjkMediaPlayer? = null
    private var mService: MediaPlayerService by Delegates.notNull()
    private var context: Context by Delegates.notNull()
    private var proxy: HttpProxyCacheServer  by Delegates.notNull()
    private var currentSong: Song? = null
    private var surface: Surface? = null

    private val listeners = ArrayList<VideoManagerListener>()
    private var currentPosition = 0
    private var networkSubscribe: Subscription? = null

    private var accompany = Accompany.BC
        set(value)
        {
            field = value
            if (getAudioTrackInfo()?.size ?: 0 > 1)
            {
                val track = videoPlayer?.getSelectedTrack(ITrackInfo.MEDIA_TRACK_TYPE_AUDIO)
                if (track == value.ordinal) return
                videoPlayer?.selectTrack(value.ordinal)
                if (isMute)
                videoPlayer?.setVolume(0f, 0f)
                else videoPlayer?.setVolume(videoVolume, videoVolume)
            }
            for (listener in listeners)
            {
                listener.onAccompanyChanged(value)
            }
        }
    private var isMute = false
        set(value)
        {
            field = value
            if (value) videoPlayer?.setVolume(0f, 0f) else videoPlayer?.setVolume(videoVolume, videoVolume)
            for (listener in listeners)
            {
                listener.onMute(value)
            }
        }
    private var isPause = false
        set(value)
        {
            field = value
            if (value) progressSubscription?.unsubscribe() else onProgress()
            for (listener in listeners)
            {
                listener.onVideoPause(value)
            }
        }

    var videoVolume = 1f
        set(value)
        {
            field = value
            Hawk.put("videoVolume", value)
            isMute = value <= 0
            for (listener in listeners)
            {
                listener.onVolumeChanged(value)
            }
        }
        get()
        {
            if (!Hawk.contains("videoVolume"))
            {
                Hawk.put("videoVolume", 1f)
            }
            return Hawk.get<Float>("videoVolume")
        }


    init
    {
        if (!Hawk.contains("videoVolume"))
        {
            Hawk.put("videoVolume", 1f)
        }
    }

    /**
     * 原伴唱
     */
    enum class Accompany
    {
        Unknown,
        YC,
        BC
    }

    /**
     * 初始化
     */
    fun initialize(context: Context)
    {
        this.context = context
        context.bindService(Intent(context, MediaPlayerService::class.java), this, Context.BIND_AUTO_CREATE)
    }

    /**
     * 初始化播放器
     */
    private fun initMediaPlayer()
    {
        videoPlayer = mService.newMediaPlayer()
        videoPlayer?.apply {
            setOnPreparedListener(this@VideoManager)
            setOnCompletionListener(this@VideoManager)
            setOnBufferingUpdateListener(this@VideoManager)
            setOnSeekCompleteListener(this@VideoManager)
            setOnErrorListener(this@VideoManager)
            setOnVideoSizeChangedListener(this@VideoManager)
            setOnInfoListener(this@VideoManager)
        }
    }

    /**
     * 开始播放视频
     */
    fun startPlayVideo(song: Song, surface: Surface)
    {
        currentSong = song
        this.surface = surface
        initMediaPlayer()
        currentPosition = 0
        videoPlayer?.dataSource = generateUrl(song)
        videoPlayer?.setSurface(surface)
        videoPlayer?.prepareAsync()
    }

    fun replayCurrent()
    {
        if (currentSong != null && surface != null)
        {
            initMediaPlayer()
            videoPlayer?.dataSource = generateUrl(currentSong!!)
            videoPlayer?.setSurface(surface)
            videoPlayer?.prepareAsync()
        }
    }

    /**
     * 设置静音
     */
    fun toggleMute()
    {
        //if (isMute) videoPlayer?.setVolume(videoVolume, videoVolume) else videoPlayer?.setVolume(0f, 0f)
        isMute = !isMute
    }

    /**
     * 设置音量
     */
    fun setVolume(v: Float)
    {
        videoVolume = v
        videoPlayer?.setVolume(videoVolume, videoVolume)
    }

    /**
     * 暂停
     */
    fun togglePause()
    {
        if (isPause) videoPlayer?.start() else videoPlayer?.pause()
        isPause = !isPause
    }

    /**
     * 重唱
     */
    fun onReplay()
    {
        videoPlayer?.seekTo(0)
    }


    /**
     * 添加回调
     */
    fun addListener(listener: VideoManagerListener)
    {
        listeners.add(listener)
    }

    fun removeListener(listener: VideoManagerListener)
    {
        listeners.remove(listener)
    }

    fun cleanListener()
    {
        listeners.clear()
    }

    /**
     * 切换原版唱
     */
    fun toggleAccompany()
    {
        accompany = if (accompany == Accompany.BC) Accompany.YC else Accompany.BC
    }

    var progressSubscription: Subscription? = null

    fun onProgress()
    {
        progressSubscription?.unsubscribe()
        videoPlayer?.let { player ->
            progressSubscription = Observable.interval((1000 - player.currentPosition.toInt() % 1000).toLong(), TimeUnit.MILLISECONDS)
                    .subscribe {
                        if (player.isPlaying)
                        {
                            this.currentPosition = player.currentPosition.toInt()
                            val duration: Int = player.duration.toInt()
                            if (duration > 0)
                            {
                                val pos = 1000 * this.currentPosition / duration
                                for (listener in listeners)
                                {
                                    listener.onCurrentPositionChanged(pos)
                                }
                            }
                        }
                    }
        }

    }

    private fun getAudioTrackInfo(): Array<ITrackInfo>?
    {
        val bundle = videoPlayer?.mediaMeta ?: return null
        val mediaMeta = IjkMediaMeta.parse(bundle)
        mediaMeta?.let {
            it.mStreams?.let {
                val trackInfos = java.util.ArrayList<IjkTrackInfo>()
                for (streamMeta in it)
                {
                    val trackInfo = IjkTrackInfo(streamMeta)
                    if (streamMeta.mType!!.equals(IjkMediaMeta.IJKM_VAL_TYPE__AUDIO, ignoreCase = true))
                    {
                        trackInfo.trackType = ITrackInfo.MEDIA_TRACK_TYPE_AUDIO
                    }
                    trackInfos.add(trackInfo)
                }
                return trackInfos.toTypedArray<ITrackInfo>()
            }
        }
        return null
    }

    private fun getVideoCacheDir(context: Context): File
    {
        val file = File("/sdcard/video-cache")
        if (!file.exists())
            file.mkdir()
        return file
    }


    override fun onCacheAvailable(cacheFile: File?, url: String?, percentsAvailable: Int)
    {
        LogUtils.e("$cacheFile $url $percentsAvailable")
    }

    override fun onPrepared(mp: IMediaPlayer?)
    {
        mp?.start()
        networkSubscribe?.unsubscribe()
        mp?.seekTo(currentPosition.toLong())
        isPause = false
        val track = videoPlayer?.getSelectedTrack(ITrackInfo.MEDIA_TRACK_TYPE_AUDIO)
        if (track != accompany.ordinal) videoPlayer?.selectTrack(accompany.ordinal)
        onProgress()
        if (isMute)
            mp?.setVolume(0f, 0f)
        else mp?.setVolume(videoVolume, videoVolume)
        for (listener in listeners)
        {
            listener.onStart(this)
        }
    }

    fun generateUrl(song: Song): String
    {
        val file = File(Environment.getExternalStorageDirectory(), song.playUrl)
        if (file.exists())
            return file.absolutePath
        else return "http://main.oksrv.com" + song.playUrl
    }

    override fun onCompletion(mp: IMediaPlayer?)
    {
        progressSubscription?.unsubscribe()
        for (listener in listeners)
        {
            listener.onAutoCompletion()
        }
    }

    override fun onBufferingUpdate(mp: IMediaPlayer?, percent: Int)
    {
    }

    override fun onSeekComplete(mp: IMediaPlayer?)
    {
    }

    override fun onVideoSizeChanged(mp: IMediaPlayer?, width: Int, height: Int, sar_num: Int, sar_den: Int)
    {
    }


    override fun onError(mp: IMediaPlayer?, what: Int, extra: Int): Boolean
    {
        for (listener in listeners)
        {
            listener.onError(what, extra)
        }
        if (what == -10000)
        {
            networkSubscribe?.unsubscribe()
            networkSubscribe = ReactiveNetwork.observeNetworkConnectivity(context)
                    .subscribeOn(Schedulers.io())
                    .filter(Connectivity.hasState(NetworkInfo.State.CONNECTED))
                    .filter(Connectivity.hasType(ConnectivityManager.TYPE_ETHERNET))
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe {
                        replayCurrent()
                    }
        }
        return what == -10000
    }

    override fun onInfo(mp: IMediaPlayer?, what: Int, extra: Int): Boolean
    {
        for (listener in listeners)
        {
            listener.onInfo(what, extra)
        }
        return false
    }

    fun onToggleService() {
        for (listener in listeners)
        {
            listener.onToggleService()
        }
    }

}
